<template>
	<div class="app">
		<h3>异步组件 官网验证:</h3>
		<h4>
			它会将接收到的 props 和插槽传给内部组件，所以你可以使用这个异步的包装组件无缝地替换原始组件，同时实现延迟加载。
		</h4>
		<div>
			<span>在 Vue3 中，异步组件加载时通常需要处理加载中、加载失败等状态以提供更好的用户体验。以下是完整的实现方案：</span>
			<button class="test" @click="clickBtn">异步组件 处理加载状态的详细例子 -> 首屏优化方案之一</button>
			<div v-if="loading">加载中...</div>
			<div v-else-if="error">加载失败: {{ error }}</div>
			<AsyncCompB v-else />
		</div>
		<h4>使用封装好的 AsyncWrapper组件 ×××××× </h4>
		<AsyncWrapper :component="() => import('../../components/a.vue')">
			<template #default="{ component: Component }">
				<Component some-prop="value" />
			</template>

			<template #loading>
				<div class="custom-loading">
					<!-- <MySpinner /> -->
					<p>正在努力加载...</p>
				</div>
			</template>

			<template #error="{ error }">
				<div class="custom-error">
					<!-- <MyErrorIcon /> -->
					<p>{{ error }}</p>
					<button @click="retry">点击重试</button>
				</div>
			</template>
		</AsyncWrapper>

		<!-- <AsyncCompA></AsyncCompA>
		<br />
		<AsyncCompB title="AsyncCompB">
			123456
		</AsyncCompB> -->
	</div>

	<div class="app">
		<h3>1、动态组件: 使用 component 标签 + is 属性</h3>
		<button @click="toggleComponent('ComponentA')">切换组件: 切换到组件A</button>
		<button @click="toggleComponent('ComponentB')">切换组件: 切换到组件B</button>
		<component :is="currentComp"></component>

		<h3>2、动态导入组件（异步加载）</h3>
		<button @click="loadComponent('a')">加载A</button>
		<button @click="loadComponent('b')">加载B</button>
		<component :is="currentComponent" v-if="currentComponent" />

		<h3>3、保持组件状态（keep-alive）</h3>
		<div class="tabs">
			<button v-for="tab in tabs" :key="tab" @click="currentTab = tab" :class="{ active: currentTab === tab }">
				{{ tab }}
			</button>
		</div>
		<keep-alive>
			<component :is="currentTabComponent" class="tab-content" />
		</keep-alive>
	</div>

	<div class="app">
		<h3>异步组件 + 同步组件</h3>
		<h5>
			运行项目,打开控制台，在进入 当前页面 的时候、同步组件 syncCom 的 js 代码已经开始执行。
			但是,只有点击 <展示异步组件> 按钮的时候，异步组件 asyncCom 的 js 代码才开始执行。
		</h5>
		<button @click="shwoSync = false;shwoAsync = true;">展示异步组件</button>
		<button @click="shwoAsync = false;shwoSync = true;">展示同步组件</button>
		<!-- 异步组件 -->
		<AsyncComponent v-if="shwoAsync" />
		<!-- 同步组件 -->
		<SyncComponent v-if="shwoSync" />
	</div>
</template>

<script setup>
	//----- 异步组件 + 同步组件 ---------------------------------------------------------------------------------------
	import {
		defineAsyncComponent,
		ref,
		computed
	} from 'vue'
	import SyncComponent from "./syncCom.vue";
	// const AsyncComponent = () => import("./asyncCom.vue"); // vue异步组件 使用方式
	const AsyncComponent = defineAsyncComponent(() => // vue3异步组件 使用方式
		import("./asyncCom.vue")
	)
	const shwoAsync = ref(false); // 是否展示异步组件
	const shwoSync = ref(false); // 是否展示同步组件

	//----- 动态组件 ---------------------------------------------------------------------------------------
	import ComponentA from './components/ComponentA.vue'
	import ComponentB from './components/ComponentB.vue'
	const currentComp = ref('ComponentA'); // 默认显示 ComponentA
	function toggleComponent(name) {
		currentComp.value = name === 'ComponentA' ?
			() => 'ComponentB' //注意: 不是 'ComponentB' 是 () => 'ComponentB'
			:
			() => 'ComponentA'
	}
	//-----2------------------------------------------------------
	const currentComponent = ref(null);

	function loadComponent(name) {
		currentComponent.value = defineAsyncComponent(() =>
			import(`../../components/${name}.vue`)
		)
	}
	//-----3------------------------------------------------------
	import Tab1 from './components/Tab1.vue'
	import Tab2 from './components/Tab2.vue'
	import Tab3 from './components/Tab3.vue'
	const tabs = ['Tab1', 'Tab2', 'Tab3']
	const currentTab = ref('Tab1')
	const componentsMap = {
		Tab1,
		Tab2,
		Tab3
	}
	const currentTabComponent = computed(() => componentsMap[currentTab.value])

	//----- 异步组件 Vue3官网两种方式: ---------------------------------------------------------------------------------------
	// 1、可以用 defineAsyncComponent 来导入 Vue 单文件组件：
	// 仅在页面需要它渲染时才会调用加载内部实际组件
	const AsyncCompA = defineAsyncComponent(() =>
		import('../../components/a.vue')
	)
	// 2、处理 加载状态 的详细例子
	const loading = ref(true)
	const error = ref(null)
	import LoadingComponent from '../../components/loadingComponent.vue'
	const AsyncCompB = defineAsyncComponent({
		// 加载函数: 需要加载的组件 (应该是一个 `Promise` 对象)
		loader: () => import('../../components/b.vue'),

		// 加载 异步组件 时使用的组件
		// loadingComponent: LoadingComponent,
		loadingComponent: {
			template: '<div>拼命加载中...</div>'
		},
		// 展示 加载组件 前的延迟时间，默认为 200ms
		delay: 3000,

		// 加载失败 后展示的组件
		errorComponent: () => import('../../components/errorComponent.vue'),
		// 如果提供了 timeout 时间限制 且 组件加载超时了,
		// 也会显示这里配置的 报错组件，默认值是：Infinity
		timeout: 10
	})

	const clickBtn = async () => {
		const res = await import("./module.js"); // import 返回值是 Promise
		console.log("res: ", res);
		let myTest = res.default;
		console.log("myTest: ", myTest);
		let target = new myTest();
		console.log("target: ", target);


		// 更精细的控制
		import('../../components/b.vue')
			.then(() => {
				loading.value = false
			})
			.catch(err => {
				error.value = err.message
				loading.value = false
			})
	}
	import AsyncWrapper from '../../components/AsyncWrapper.vue'
	// import MySpinner from '../../components/MySpinner.vue'
	// import MyErrorIcon from '../../components/MyErrorIcon.vue'
</script>

<style scoped>
	.tabs {
		margin-bottom: 20px;
	}

	.tabs button {
		padding: 8px 16px;
		margin-right: 5px;
		cursor: pointer;
	}

	.tabs button.active {
		background-color: #42b983;
		color: white;
	}

	.tab-content {
		border: 1px solid #eee;
		padding: 20px;
	}
</style>